package cn.biplam.common.wrapper.treeFlat.pcTree;

import java.util.ArrayList;
import java.util.List;

import cn.biplam.common.tools.TOOLS;
import cn.biplam.common.wrapper.ErrorMessage;
import cn.biplam.common.wrapper.KvMap;
import cn.biplam.common.wrapper.Result;
import cn.biplam.common.wrapper.treeFlat.PcNode;
import cn.biplam.common.wrapper.treeFlat.flat.callback.CompareCallback;
import cn.biplam.common.wrapper.treeFlat.flat.callback.CompareCallback.Compare_Type;
import cn.biplam.common.wrapper.treeFlat.pcTree.callback.OperatorTypeCallback;
import cn.biplam.common.wrapper.treeFlat.pcTree.callback.VirNodeCallback;

/**
 *  父子关系数据转树形关系
 * @author XiaoRuxing Nov 19, 20097:32:59 PM
 * @version 1.0
 */
public class PcTreeImp implements PcTree{

	/** 是否有过滤节点 */
	private boolean isHaveFilter = false;
	/** 是否有包含节点 */
	private boolean isHaveInclude = false;
	/** 是否添加虚拟节点 */
	private boolean isAddVirNode = false;
	/** 虚拟节点名称回调器 */
	private VirNodeCallback virNodeCallback;

	/** 树形关系数据 */
	private Result treeData;
	//父亲儿子
	private KvMap<String, ArrayList<String>> nodeChilds;
	//节点名称
	private KvMap<String, String> nodeName;
	//节点业务级别
	private KvMap<String, String> nodeLevel;
	//节点操作类型
	private KvMap<String, String> nodeOperatorType;
	//所有节点列表
	private ArrayList<String> nodeList ;
	//根节点列表
	private ArrayList<String> rootList ;
	//虚拟节点列表
	private ArrayList<PcNode> virtualNodes;

	/** 当前父子关系树 */
	private PcNode tree;

//	/**************************
//	* 数据字段名
//	**************************/
	/** 记录ID字段名 */
	private String nodeIdField;
	/** 父亲ID字段名 */
	private String nodeParentField;
	/** 节点名称字段名 */
	private String nodeNameField;
	/** 业务级别字段名 */
	private String nodeLevelField;
	/** 操作类型回调器 */
	private OperatorTypeCallback operatorType;

	/** 包含节点列表 */
	private List<String> includeList;
	/** 过滤节点列表 */
	private List<String> filterList;
	/** 父亲条件回调器 */
	private CompareCallback parentConditionCallback;

	public PcTreeImp(){
		this._init();
	}

	/**
	 *  创建父子关系转树形关系类
	 * @param treeData    父子关系数据集<BR>父子关系树的所有数据,包含ID,父ID,节点名称和业务级别(可选)     
	 * @param nodeIdField          记录ID字段名
	 * @param nodeParentIdField    父亲ID字段名
	 * @param nodeNameField        节点名称字段名
	 * @param nodeLevelField       业务级别字段名(可以为空)
	 */
	public PcTreeImp(Result treeData,
			String nodeIdField,
			String nodeParentIdField,
			String nodeNameField,
			String nodeLevelField){
		this.setPcData(treeData);
		this.setFieldMap(nodeIdField, 
				nodeParentIdField, 
				nodeNameField, 
				nodeLevelField);
		_init();
	}

	/**
	 *  设置父子关系数据集
	 * @param treeData 父子关系数据集<BR>
	 *                 父子关系树的所有数据
	 */
	public void setPcData(Result treeData){
		this.treeData = treeData;
	}

	/**
	 *  设置父子关系数据集字段名称映射<BR>
	 *  记录ID,父亲ID,节点名称和业务级别对应的字段名称
	 * @param nodeIdField     记录ID字段名
	 * @param nodeParentIdField 父亲ID字段名
	 * @param nodeNameField   节点名称字段名
	 * @param nodeLevelField  业务级别字段名
	 */
	public void setFieldMap(String nodeIdField,
			String nodeParentIdField,
			String nodeNameField, 
			String nodeLevelField){
		this.setNodeIdFieldName(nodeIdField);
		this.setNodeParentIdFieldName(nodeParentIdField);
		this.setNodeNameFieldName(nodeNameField);
		this.setNodeLevlelFieldName(nodeLevelField);
	}

	/**
	 *  设置是否按业务级别设置虚拟节点<BR>
	 *  当前节点的级别比业务级别高时,在当前节点与其父亲之间加入虚拟节点以匹配业务级别 
	 * @param isAddVirNode 是否设置虚拟节点
	 */
	public void setIsAddVirNode(boolean isAddVirNode){
		this.isAddVirNode = isAddVirNode;
	}

	/**
	 *  设置虚拟节点名称回调器 
	 * @param virCallback 虚拟节点名称回调器
	 */
	public void setVirnCallBack(VirNodeCallback virCallback){
		this.virNodeCallback = virCallback;
	}

	/**
	 *  设置节点操作类型回调器<BR>
	 *  节点操作类型回调器用于生成节点的操作类型
	 */
	public void setOperatorCallback(OperatorTypeCallback callback){
		this.operatorType = callback;
	}

	/**
	 *  设置节点ID字段名称
	 * @param nodeIdField
	 */
	public void setNodeIdFieldName(String nodeIdField){
		this.nodeIdField = nodeIdField;
	}

	/**
	 *   设置节点父亲ID字段名称
	 * @param nodeIdField 父节点ID字段
	 */
	public void setNodeParentIdFieldName(String nodeParentIdField){
		this.nodeParentField = nodeParentIdField;
	}

	/**
	 *   设置节点名称字段
	 * @param nodeNameField 节点名称字段
	 */
	public void setNodeNameFieldName(String nodeNameField){
		this.nodeNameField = nodeNameField;
	}

	/**
	 *   设置节点业务级别字段名称
	 * @param nodeLevelField 业务级别字段
	 */
	public void setNodeLevlelFieldName(String nodeLevelField){
		this.nodeLevelField = nodeLevelField;
	}

	/**
	 * 设置过滤数据列表 
	 * @param filterList 被过滤的节点列表(存储的内容为节点ID)
	 */
	public void setDataFilter(List<String> filterList){
		this.filterList = filterList;
		this.isHaveFilter = (filterList ==null || filterList.size()<1)?false:true;
	}

	/**
	 *  设置包含节点列表<BR>
	 *  列表节点的所有子节点作为当前树的数据,即根节点为列表中的节点(节点数不应过多)<BR>
	 *  与节点过滤条件同时设置时,只能过滤当前节点列表的子节点<BR>
	 * @param includeList 被包含节点的列表
	 */
	public void setInclude(List<String> includeList){
		this.includeList = includeList;
		this.isHaveInclude = (includeList==null || includeList.size()<1)?false:true;
	}

	/**
	 *  设置根节点条件回调器
	 * @param parentCallback 根节点条件回调器
	 */
	public void setRootNodeCallBack(CompareCallback parentCallback){
		this.parentConditionCallback = parentCallback;
	}

	/**
	 * @return 当前树
	 */
	public PcNode get() throws ErrorMessage{
		if(this.tree==null)this.tree = createTree();
		return this.tree;
	}

	/**
	 * @return 获取字段映射关系
	 */
	public KvMap<String, String> getFieldMap() {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 创建树(根节点个数决定树个数)<BR>
	 * 获取数据集中所有根节点(目前判断根节点条件为父节点为空)<BR>
	 * 遍历根节点,创建指定根节点的树<BR>
	 * @throws ErrorMessage 
	 */
	private PcNode createTree() throws ErrorMessage{
		PcNode head = new PcNode();//创建树的根
		//获取根节点
		getRoot();
		//创建节点
		for(String nodeId:this.rootList){
			createRoot(nodeId, head);
		}
		return head;
	}

	/**
	 *  获取所有根节点
	 * @throws ErrorMessage 
	 */
	private void getRoot() throws ErrorMessage{
		if(this.treeData==null)return;
		//遍历数据前设置游标
		this.treeData.beforeFirst();
		//设置根节点比较器
		if(this.parentConditionCallback == null){
			this.parentConditionCallback = new CompareCallback(null,this.nodeParentField,Compare_Type.Equal);
		}
		String nodeId;
		String nodeParent;
		while(this.treeData.next()){
			//获取节点ID
			nodeId = this.treeData.getString(this.nodeIdField);
			//获取父节点ID
			nodeParent = this.treeData.getString(this.nodeParentField);
			this.nodeList.add(nodeId);
			//存储节点的儿子们
			ArrayList<String> curChilds = null;
			curChilds = this.nodeChilds.getByKey(nodeParent);
			if(curChilds ==null){
				curChilds = new ArrayList<String>();
				this.nodeChilds.put(nodeParent, curChilds);
			}
			curChilds.add(nodeId);

			this.nodeName.put(nodeId, this.treeData.getString(this.nodeNameField));
			//获取节点业务级别
			if(this.nodeLevelField!=null){
				this.nodeLevel.put(nodeId, this.treeData.getString(this.nodeLevelField));
			}
			//获取节点类型
			if(this.operatorType!=null){
				this.nodeOperatorType.put(nodeId, 
						operatorType.getType(this.treeData));
			}
			//获取根节点
			if(isRoot(nodeId, nodeParent)){
				this.rootList.add(nodeId);
			}
		}
	}

	/**
	 *  获取节点是否为根节点<BR>
	 *  判断标准是:父亲节点ID为空时为根节点,父节点ID不为空时不是根节点<BR><B>可根据实际情况需要修改</B><BR>
	 *  如果包含节点列表有数据则除包含列表中的节点外其他节点为非根节点
	 * @param nodeId     节点ID
	 * @param nodeParent 父亲节点iD
	 * @return 节点是否根节点
	 * @throws ErrorMessage 
	 */
	private boolean isRoot(String nodeId,String nodeParent) throws ErrorMessage{
		//有包含节点
		if(isHaveInclude){
			return includeList.indexOf(nodeId)>=0;
		}
		//根据根节点条件回调器获取内容
		return this.parentConditionCallback.get(this.treeData);
	}

	/**
	 *  创建根节点<BR>
	 *  创建指定根节点的树<BR>
	 * @throws ErrorMessage 
	 */
	private void createRoot(String rootId, PcNode head) throws ErrorMessage{
		//创建指定根节点
		PcNode node = createNode(rootId, 
				nodeName.getByKey(rootId), 
				nodeLevel.getByKey(rootId), 
				nodeOperatorType.getByKey(rootId),
				head);
		//创建根节点的所有儿子
		doing(node);
	}

	/**
	 *  递归创建所有子节点
	 * @throws ErrorMessage 
	 */
	private void doing(PcNode parent) throws ErrorMessage{
		if(parent==null)return;
		ArrayList<String> childs = nodeChilds.getByKey(parent.nodeID);
		if(childs==null)return;
		for(String nodeId: childs){
			doing(
					createNode(nodeId, 
							nodeName.getByKey(nodeId), 
							nodeLevel.getByKey(nodeId), 
							nodeOperatorType.getByKey(nodeId),
							parent)
			);
		}
	}

	/**
	 *  判断当前节点未被过滤
	 *  获取需要添加虚拟节点个数N,N大于0则创建N个虚拟节点
	 *  创建节点 new TreeNode(nodeId,nodeName,nodeLevel)
	 *  将节点放入父亲的儿子中(如果有虚拟节点将当前节点放入虚拟节点的儿子中)
	 * @return 节点
	 */
	private PcNode createNode(String nodeId,
			String nodeName,
			String nodeLevel,
			String operatorType,
			PcNode nodeParent){
		//节点ID或者父节点ID为空不创建节点
		//被过滤节点不创建
		if(nodeId == null 
				|| nodeParent==null
				|| isFilter(nodeId)
		)return null;
		//创建节点
		PcNode node= _createNode(nodeId, nodeName, nodeLevel,operatorType, nodeParent);
		//获取需要添加虚拟节点的个数
		int virNumb = addVirn(node, nodeParent);
		PcNode tempParent = nodeParent;
		//需要添加虚拟节点
		if(virNumb>0){
			for(int i=0;i<virNumb;i++){
				tempParent =
					createVirNode(nodeId, nodeName, nodeLevel,operatorType, tempParent);//创建虚拟节点
			}
			//从新设置节点的父亲
			node.parent = tempParent;
		}
		//将节点放入父亲的儿子中
		addNodeChild(tempParent, node);
		return node;

	}

	/**
	 * @return 需要添加虚拟节点个数
	 */
	private int addVirn(PcNode curNode,PcNode parentNode){
		//不需要添加虚拟节点
		if(!this.isAddVirNode)return -1;
		//返回当前节点与父亲节点的层级差超过1的部分
		return curNode.nodeLevel-parentNode.nodeLevel-1;
		//添加虚拟节点标志
	}

	/**
	 *  创建虚拟节点
	 */
	private PcNode createVirNode(String nodeId, 
			String nodeName, 
			String nodeLevel,
			String operatorType,
			PcNode nodeParent){
		//调用虚拟节点名称回调器获取虚拟节点名称
		if(this.virNodeCallback!=null)
			nodeName = virNodeCallback.getName(nodeId);
		//创建虚拟节点
		PcNode node = _createNode(nodeId, 
				nodeName, 
				nodeLevel,
				operatorType,
				nodeParent);
//		将虚拟节点放入父亲的儿子中
		addNodeChild(nodeParent, node);
//		将虚拟节点存入虚拟节点容器
		if(this.virtualNodes==null)
			this.virtualNodes = new ArrayList<PcNode>();
		this.virtualNodes.add(node);
		return node;
	}

	/**
	 * @return 创建节点
	 */
	private PcNode _createNode(String nodeId,
			String nodeName,
			String nodeLevel,
			String operatorType,
			PcNode nodeParent){
		PcNode node = new PcNode();
		setNodeId(node, nodeId);
		setNodeName(node, nodeName);
		setNodeLevel(node, nodeLevel);
		setNodeOperatorType(node, operatorType);
		setNodeParent(node, nodeParent);
		return node;
	}

	/**
	 * @return 当前节点是否被过滤
	 */
	private boolean isFilter(String nodeId){
		//节点是否在过滤列表内
		return (!isHaveFilter || this.filterList.indexOf(nodeId)<0)?false:true;
	}

	/**
	 *  添加指定树节点的儿子
	 * @param parent 父亲节点
	 * @param child  儿子节点
	 */
	private void addNodeChild(PcNode parent, PcNode child){
		if(parent.children == null){
			parent.children = new ArrayList<PcNode>();
		}
		parent.children.add(child);
	}

	/**
	 *  设置节点名称
	 */
	private void setNodeId(PcNode node,String nodeId){
		node.nodeID = nodeId;
	}

	/**
	 *  设置节点名称
	 */
	private void setNodeName(PcNode node,String nodeName){
		node.nodeName = nodeName;
	}

	/**
	 *  设置节点父亲
	 */
	private void setNodeParent(PcNode node,PcNode nodeParent){
		node.parent = nodeParent;
	}

	/**
	 *  设置节点级别
	 */
	private void setNodeLevel(PcNode node,String nodeLevel){
		if(nodeLevel!=null)
			node.nodeLevel = TOOLS.stringToInt(nodeLevel);
	}

	/**
	 *  设置节点操作类型
	 */
	private void setNodeOperatorType(PcNode node,String operatorType){
		if(nodeLevel!=null)
			node.nodeOperatorType = operatorType;
	}

	/**
	 *  初始化容器
	 */
	private void _init(){
		nodeName = new KvMap<String, String>();
		nodeLevel = new KvMap<String, String>();
		nodeOperatorType = new KvMap<String, String>();
		nodeList = new ArrayList<String>();
		rootList = new ArrayList<String>();
		nodeChilds = new KvMap<String, ArrayList<String>>();
	}
}
